`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/16 09:29:39
// Design Name: 
// Module Name: regfile
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module register_file_R5W1
#(
    parameter REG_BITS = 32,  // default to 8b wide registers
    ADDR_BITS = 5            // default to 8 registers total
)
(
    input CLK,                                // clock signal
    input wr0_en,                              // write enable
    input wire [ADDR_BITS - 1 : 0] wr0_addr,   // write address
    input wire [REG_BITS - 1 : 0] wr0_data,    // input port

    input wire [ADDR_BITS - 1 : 0] r0_addr,    // read address
    input wire [ADDR_BITS - 1 : 0] r1_addr,    // read address
    input wire [ADDR_BITS - 1 : 0] r2_addr,    // read address
    input wire [ADDR_BITS - 1 : 0] r3_addr,    // read address
    input wire [ADDR_BITS - 1 : 0] r4_addr,    // read address

    output wire [REG_BITS - 1 : 0] r0_data,     // output port
    output wire [REG_BITS - 1 : 0] r1_data,     // output port
    output wire [REG_BITS - 1 : 0] r2_data,     // output port
    output wire [REG_BITS - 1 : 0] r3_data,     // output port
    output wire [REG_BITS - 1 : 0] r4_data     // output port
);

// declare an array of registers
reg [REG_BITS - 1 : 0] register_array [2 ** ADDR_BITS - 1 : 0];

// handle synchronous write operation
always @(posedge CLK) begin 
    if (wr0_en)
        begin 
        register_array[wr0_addr] <= wr0_data;
        end
end        
// handle read operation
assign r0_data = register_array[r0_addr];    
assign r1_data = register_array[r1_addr];    
assign r2_data = register_array[r2_addr];    
assign r3_data = register_array[r3_addr];    
assign r4_data = register_array[r4_addr];  

endmodule

module register_file_R4W1
#(
    parameter REG_BITS = 32,  // default to 8b wide registers
    ADDR_BITS = 5            // default to 8 registers total
)
(
    input CLK,                                // clock signal
    input wr0_en,                              // write enable
    input wire [ADDR_BITS - 1 : 0] wr0_addr,   // write address
    input wire [REG_BITS - 1 : 0] wr0_data,    // input port

    input wire [ADDR_BITS - 1 : 0] r0_addr,    // read address
    input wire [ADDR_BITS - 1 : 0] r1_addr,    // read address
    input wire [ADDR_BITS - 1 : 0] r2_addr,    // read address
    input wire [ADDR_BITS - 1 : 0] r3_addr,    // read address

    output wire [REG_BITS - 1 : 0] r0_data,     // output port
    output wire [REG_BITS - 1 : 0] r1_data,     // output port
    output wire [REG_BITS - 1 : 0] r2_data,     // output port
    output wire [REG_BITS - 1 : 0] r3_data      // output port
);

// declare an array of registers
reg [REG_BITS - 1 : 0] register_array [2 ** ADDR_BITS - 1 : 0];

// handle synchronous write operation
always @(posedge CLK) begin 
    if (wr0_en)
        begin 
        register_array[wr0_addr] <= wr0_data;
        end
end        
// handle read operation
assign r0_data = register_array[r0_addr];    
assign r1_data = register_array[r1_addr];    
assign r2_data = register_array[r2_addr];    
assign r3_data = register_array[r3_addr];    

endmodule

module register_file_R1W1
#(
    parameter REG_BITS = 32,  // default to 8b wide registers
    ADDR_BITS = 5            // default to 8 registers total
)
(
    input CLK,                                // clock signal
    input wr0_en,                              // write enable
    input wire [ADDR_BITS - 1 : 0] wr0_addr,   // write address
    input wire [REG_BITS - 1 : 0] wr0_data,    // input port

    input wire [ADDR_BITS - 1 : 0] r0_addr,    // read address
    output wire [REG_BITS - 1 : 0] r0_data     // output port
);

// declare an array of registers
reg [REG_BITS - 1 : 0] register_array [2 ** ADDR_BITS - 1 : 0];

// handle synchronous write operation
always @(posedge CLK) begin 
    if (wr0_en)
        begin 
        register_array[wr0_addr] <= wr0_data;
        end
end        
// handle read operation
assign r0_data = register_array[r0_addr];    

endmodule

module register_file_R2W1 #(
    parameter REG_BITS = 32,  // default to 8b wide registers
    parameter ADDR_BITS = 5            // default to 8 registers total
    )(
    input CLK,                                // clock signal
    input wr0_en,                              // write enable
    input [ADDR_BITS - 1 : 0] wr0_addr,   // write address
    input [REG_BITS - 1 : 0] wr0_data,    // input port

    input [ADDR_BITS - 1 : 0] r0_addr,    // read address
    input [ADDR_BITS - 1 : 0] r1_addr,    // read address
    output [REG_BITS - 1 : 0] r0_data,     // output port
    output [REG_BITS - 1 : 0] r1_data     // output port
);

    // declare an array of registers (all registers)
    reg [REG_BITS - 1 : 0] register_array [2 ** ADDR_BITS - 1 : 0];

    // handle synchronous write operation
    always @(posedge CLK) begin 
        if (wr0_en) begin 
            register_array[wr0_addr] <= wr0_data;
        end
    end
    
    assign r0_data = (wr0_addr == r0_addr) ? wr0_data  : register_array[r0_addr];    
    assign r1_data = (wr0_addr == r1_addr) ? wr0_data  : register_array[r1_addr]; 
    
endmodule


module register_file_R5W2
#(
    parameter REG_BITS = 32,  // default to 8b wide registers
    ADDR_BITS = 5            // default to 8 registers total
)
(
    input CLK,                                // clock signal
    input wr0_en,                              // write enable
    input wr1_en,
    input wire [ADDR_BITS - 1 : 0] wr0_addr,   // write address
    input wire [REG_BITS - 1 : 0] wr0_data,    // input port
    input wire [ADDR_BITS - 1 : 0] wr1_addr,   // write address
    input wire [REG_BITS - 1 : 0] wr1_data,    // input port

    input wire [ADDR_BITS - 1 : 0] r0_addr,    // read address
    input wire [ADDR_BITS - 1 : 0] r1_addr,    // read address
    input wire [ADDR_BITS - 1 : 0] r2_addr,    // read address
    input wire [ADDR_BITS - 1 : 0] r3_addr,    // read address
    input wire [ADDR_BITS - 1 : 0] r4_addr,    // read address

    output wire [REG_BITS - 1 : 0] r0_data,     // output port
    output wire [REG_BITS - 1 : 0] r1_data,     // output port
    output wire [REG_BITS - 1 : 0] r2_data,     // output port
    output wire [REG_BITS - 1 : 0] r3_data,     // output port
    output wire [REG_BITS - 1 : 0] r4_data     // output port
);

// declare an array of registers
reg [REG_BITS - 1 : 0] register_array [2 ** ADDR_BITS - 1 : 0];

// handle synchronous write operation
always @(posedge CLK) begin 
    if (wr0_en)
        begin 
        register_array[wr0_addr] <= wr0_data;
        end
    
    if (wr1_en)
        begin 
        register_array[wr1_addr] <= wr1_data;
        end
    
end        
// handle read operation
assign r0_data = register_array[r0_addr];    
assign r1_data = register_array[r1_addr];    
assign r2_data = register_array[r2_addr];    
assign r3_data = register_array[r3_addr];    
assign r4_data = register_array[r4_addr];  

endmodule